#version 330 core

struct DirLight
{
	vec3 direction;

	vec3 ambient;
	vec3 diffuse;
	vec3 specular;
};

struct PointLight
{
	vec3 position;

	vec3 ambient;
	vec3 diffuse;
	vec3 specular;

	float constant;
	float linear;
	float quadratic;
};

struct SpotLight
{
	vec3 position;
	vec3 direction;

	vec3 ambient;
	vec3 diffuse;
	vec3 specular;

	float innerCutoff;
	float outerCutoff;
};

struct Material
{
	sampler2D diffuse;
	sampler2D specular;
	float shininess;
};

in vec2 texPos;
in vec3 normal;
in vec3 fragPos;

out vec4 FragColor;

uniform DirLight dirLight;
uniform PointLight pointLight;
//uniform PointLight pointLight[4];
uniform SpotLight spotLight;
uniform Material material;
uniform vec3 viewPos;
uniform bool isLight;

vec3 DoDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
	vec3 lightDir = normalize(-light.direction);

	vec3 ambient = texture(material.diffuse, texPos).rgb * light.ambient;

	float diff = max(dot(normal, lightDir), 0.0f);
	vec3 diffuse = diff * texture(material.diffuse, texPos).rgb * light.diffuse;

	vec3 reflectDir = reflect(-lightDir, normal);
	float spec = pow(max(dot(viewDir, reflectDir), 0.0f), material.shininess);
	vec3 specular = spec * texture(material.specular, texPos).rgb * light.specular;

	return ambient + diffuse + specular;
}

vec3 DoPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
	vec3 lightDir = normalize(light.position - fragPos);
	float distance = length(light.position - fragPos);
	float attenuation = 1.0f / (light.constant + light.linear * distance + light.quadratic * distance * distance);

	vec3 ambient = texture(material.diffuse, texPos).rgb * light.ambient;

	float diff = max(dot(lightDir, normal), 0.0f);
	vec3 diffuse = diff * texture(material.diffuse, texPos).rgb * light.diffuse;

	vec3 reflectDir = reflect(-lightDir, normal);
	float spec = pow(max(dot(reflectDir, viewDir), 0.0f), material.shininess);
	vec3 specular = spec * texture(material.specular, texPos).rgb * light.specular;

	return (ambient + diffuse + specular) * attenuation;
}

vec3 DoSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
	vec3 lightDir = normalize(light.position - fragPos);
	float theta = dot(lightDir, normalize(-light.direction));

	vec3 ambient = texture(material.diffuse, texPos).rgb * light.ambient;

	if (theta > light.outerCutoff)
	{
		float theta     = dot(lightDir, normalize(-light.direction));
		float epsilon   = light.innerCutoff - light.outerCutoff;
		float intensity = clamp((theta - light.outerCutoff) / epsilon, 0.0, 1.0);

		float diff = max(dot(lightDir, normal), 0.0f);
		vec3 diffuse = diff * texture(material.diffuse, texPos).rgb * light.diffuse;

		vec3 reflectDir = reflect(-lightDir, normal);
		float spec = pow(max(dot(reflectDir, viewDir), 0.0f), material.shininess);
		vec3 specular = spec * texture(material.specular, texPos).rgb * light.specular;

		return (diffuse + specular) * intensity + ambient;
	}

	return ambient;
}

void main()
{
	if(isLight)
	{
		FragColor = vec4(1.0f, 1.0f, 1.0f, 1.0f);
	}
	else
	{

		vec3 norm = normalize(normal);
		vec3 viewDir = normalize(viewPos - fragPos);

		vec3 dirLightColor = DoDirLight(dirLight, norm, viewDir);
		vec3 pointLightColor = DoPointLight(pointLight, norm, fragPos, viewDir);
		vec3 spotLightColor = DoSpotLight(spotLight, norm, fragPos, viewDir);

		FragColor = vec4(dirLightColor + pointLightColor + spotLightColor, 1.0f);
	}
}

